Imports System
Imports System.Data
Imports System.Collections.Generic

Imports Framework

<Serializable(), CLSCompliant(True)> _
Partial Public Class CLinkList : Inherits List(Of CLink)

#Region "Constructors"
    'Basic constructor
    Public Sub New()
        MyBase.New()  
    End Sub
    
    'More efficient memory-allocation if size is known
    Public Sub New(ByVal capacity As Integer)
        MyBase.New(capacity)  
    End Sub
    
    'Shares the index (if its already been computed)
    Public Sub New(ByVal list As CLinkList)
        MyBase.New(list)
        _index = list._index 
    End Sub

    'Generic list (eg. from paging control), have to assume type
    Public Sub New(ByVal list As IList)
        MyBase.New(list.Count)
        For Each i As CLink In list
            MyBase.Add(i)
        Next
    End Sub
#End Region

#Region "Top/Bottom/Page"
    Public Function Top(ByVal count As Integer) As CLinkList
        If count >= Me.Count Then Return Me
        Return New CLinkList(CUtilities.Page(Me, count, 0))
    End Function
    Public Function Bottom(ByVal count As Integer) As CLinkList
        If count > Me.Count Then count = Me.Count
        Return New CLinkList(Me.GetRange(Me.Count - count - 1, count))
    End Function
    Public Function Page(pageSize As Integer, pageIndex As Integer) As CLinkList
        Return New CLinkList( CUtilities.Page(Me, pageSize, pageIndex) )
    End Function
#End Region

#Region "BulkEditLogic"
    Public Function HaveSameValue(ByVal propertyName As String) As Boolean
        Return CReflection.HaveSameValue(Me, propertyName)
    End Function
    Public Sub SetSameValue(ByVal propertyName As String, ByVal value As Object)
        CReflection.SetSameValue(Me, propertyName, value)
    End Sub
#End Region

#Region "SortBy"
    'Public
    Public Function SortBy(ByVal propertyName As String, Optional ByVal descending As Boolean = False) As CLinkList
        Dim copy As New CLinkList(Me)
        If Me.Count = 0 Then
            Return copy
        End If
        copy.Sort(New CLinkList_SortBy(propertyName, descending, Me))
        Return copy
    End Function
    'Private 
    Private Class CLinkList_SortBy : Inherits CReflection.GenericSortBy : Implements IComparer(Of CLink)
        Public Sub New(ByVal propertyName As String, ByVal descending As Boolean, ByVal list As IList)
            MyBase.New(propertyName, descending, list)
        End Sub
        Public Overloads Function Compare(ByVal x As CLink, ByVal y As CLink) As Integer Implements IComparer(Of CLink).Compare
            Return MyBase.Compare(x, y)
        End Function
    End Class
#End Region

#Region "SaveAll/DeleteAll"
    'Use default connection (may be overridden in base class)
    Public Sub SaveAll()
        If Me.Count = 0 Then Exit Sub
        SaveAll(Me(0).DataSrc)
    End Sub
    Public Sub DeleteAll()
        If Me.Count = 0 Then Exit Sub
        DeleteAll(Me(0).DataSrc)
    End Sub
    
    'Use connection supplied
    Public Sub SaveAll(dataSrc as CDatasrc)
        dataSrc.BulkSave(Me)
    End Sub
    Public Sub DeleteAll(dataSrc as CDatasrc)
        dataSrc.BulkDelete(Me)
    End Sub

    'Use transaction supplied
    Public Sub SaveAll(ByVal txOrNull As IDbTransaction)
        For Each i As CLink In Me
            i.Save(txOrNull)
        Next
    End Sub
    Public Sub DeleteAll(ByVal txOrNull As IDbTransaction)
        For Each i As CLink In Me
            i.Delete(txOrNull)
        Next
    End Sub

    'Use a specified isolation level
    Public Sub SaveAll(ByVal txIsolationLevel As IsolationLevel)
        If Me.Count = 0 Then Exit Sub
        SaveAll(Me(0).DataSrc, txIsolationLevel)
    End Sub
    Public Sub DeleteAll(ByVal txIsolationLevel As IsolationLevel)
        If Me.Count = 0 Then Exit Sub
        DeleteAll(Me(0).DataSrc, txIsolationLevel)
    End Sub
    
    'Use a specified connection and isolation level
    Public Sub SaveAll(ByVal dataSrc as CDatasrc, ByVal txIsolationLevel As IsolationLevel)
        dataSrc.BulkSave(Me, txIsolationLevel)
    End Sub
    Public Sub DeleteAll(ByVal dataSrc as CDatasrc, ByVal txIsolationLevel As IsolationLevel)
        dataSrc.BulkDelete(Me, txIsolationLevel)
    End Sub
#End Region

#Region "Cache-Control"
    'Main Logic
    Public Shadows Sub Add(ByVal item As CLink)
        If Not IsNothing(_index) Then
            Dim k As String = Key(item.LinkFromItemId, item.LinkToItemId)
            If Not _index.ContainsKey(k) Then
                _index(k) = item
            End If
        End If
        MyBase.Add(item)
    End Sub
    Public Shadows Sub Remove(ByVal item As CLink)
        If Not IsNothing(_index) Then
            Dim k As String = Key(item.LinkFromItemId, item.LinkToItemId)
            If _index.ContainsKey(k) Then
                _index.Remove(k)
            End If
        End If
        MyBase.Remove(item)
    End Sub
    
    'Supplementary List Overloads
    Public Shadows Sub Add(ByVal itemsToAdd As IList(Of CLink))
        For Each i As CLink In itemsToAdd
            Add(i)
        Next
    End Sub
    Public Shadows Sub Remove(ByVal itemsToRemove As IList(Of CLink))
        For Each i As CLink In itemsToRemove
            Remove(i)
        Next
    End Sub
#End Region

#Region "Main Index (on LinkFromItemId, LinkToItemId)
    Default Public Overloads ReadOnly Property Item(ByVal linkFromItemId As Integer, ByVal linkToItemId As Integer) As CLink
        Get
            Return GetById(linkFromItemId, linkToItemId)
        End Get
    End Property
    Public Shared Function Key(ByVal linkFromItemId As Integer, ByVal linkToItemId As Integer) As String
        Return linkFromItemId.ToString() + "_" + linkToItemId.ToString()
    End Function
    Public Function GetById(ByVal linkFromItemId As Integer, ByVal linkToItemId As Integer) As CLink
        Dim c As CLink = Nothing
        Dim k As String = Key(linkFromItemId, linkToItemId)
        Index.TryGetValue(k, c)
        Return c
    End Function
    <NonSerialized()> _
    Private _index As Dictionary(Of String, CLink)
    Private ReadOnly Property Index() As Dictionary(Of String, CLink)
        Get
            If Not IsNothing(_index) Then
                If _index.Count = Me.Count Then
                    Return _Index
                End If
            End If

            _index = New Dictionary(Of String, CLink)(Me.Count)
            For Each i As CLink in Me
                _index(Key(i.LinkFromItemId, i.LinkToItemId)) = i
            Next    
            Return _index
        End Get
    End Property
#End Region
    
#Region "Foreign-Key Indices (Subsets)
    'Index by LinkFromItemId
    Public Function GetByFromItemId(ByVal fromItemId As Integer) As CLinkList
        Dim temp As CLinkList = Nothing
        If Not IndexByFromItemId.TryGetValue(fromItemId, temp) Then
            temp = New CLinkList()
            IndexByFromItemId(fromItemId) = temp
        End If
        Return temp
    End Function
    <NonSerialized()> _
    Private _indexByFromItemId As Dictionary(Of Integer, CLinkList)
    Private ReadOnly Property IndexByFromItemId() As Dictionary(Of Integer, CLinkList)
        Get
            If IsNothing(_indexByFromItemId) Then
                'Instantiate
                Dim index As New Dictionary(Of Integer, CLinkList)()

                'Populate
                Dim temp As CLinkList = Nothing
                For Each i As CLink in Me
                    If Not index.TryGetValue(i.LinkFromItemId, temp) Then
                        temp = New CLinkList()
                        index(i.LinkFromItemId) = temp
                    End If
                    temp.Add(i)
                Next

                'Store
                _indexByFromItemId = index
            End If
            Return _indexByFromItemId
        End Get
    End Property

    'Index by LinkToItemId
    Public Function GetByToItemId(ByVal toItemId As Integer) As CLinkList
        Dim temp As CLinkList = Nothing
        If Not IndexByToItemId.TryGetValue(toItemId, temp) Then
            temp = New CLinkList()
            IndexByToItemId(toItemId) = temp
        End If
        Return temp
    End Function
    <NonSerialized()> _
    Private _indexByToItemId As Dictionary(Of Integer, CLinkList)
    Private ReadOnly Property IndexByToItemId() As Dictionary(Of Integer, CLinkList)
        Get
            If IsNothing(_indexByToItemId) Then
                'Instantiate
                Dim index As New Dictionary(Of Integer, CLinkList)()

                'Populate
                Dim temp As CLinkList = Nothing
                For Each i As CLink in Me
                    If Not index.TryGetValue(i.LinkToItemId, temp) Then
                        temp = New CLinkList()
                        index(i.LinkToItemId) = temp
                    End If
                    temp.Add(i)
                Next

                'Store
                _indexByToItemId = index
            End If
            Return _indexByToItemId
        End Get
    End Property

#End Region

End Class
